﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using ScrumTable.Common;
using ScrumTable.Common.FileSystem;
using ScrumTable.Common.Net;
using ScrumTable.Common.Security;

#endregion

namespace ScrumTable.DL.Data.TFS.Services.TeamFoundation
{
    /// <summary>
    /// Represents a wrapper for attachment handling facilities.
    /// </summary>
    public class TfsAttachmentHandler : IEnumerable<TfsAttachment>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly WorkItem _workItem;
        private readonly ConnectionInformation _connectionInfo;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="TfsAttachmentHandler"/> class.
        /// </summary>
        /// <param name="workItem">Specifies the parent work item instance.</param>
        /// <param name="connectionInfo">Specifies the connection information.</param>
        public TfsAttachmentHandler(WorkItem workItem, ConnectionInformation connectionInfo)
        {
            PreCondition.AssertNotNull(workItem, "workItem");
            PreCondition.AssertNotNull(connectionInfo, "connectionInfo");

            _workItem = workItem;
            _connectionInfo = connectionInfo;
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------


        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<TfsAttachment> GetEnumerator()
        {
            for (int i = 0; i < _workItem.Attachments.Count; ++i)
            {
                yield return new TfsAttachment(_workItem.Attachments[i]);
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Tries the get the uri of the newest attachment with the specified name.
        /// </summary>
        /// <param name="attachmentName">Name of the attachment to retrieve.</param>
        /// <returns>Returns the attachment uri or a null reference, if the attachment could not be found.</returns>
        public Uri TryGetAttachmentUri(string attachmentName)
        {
            Attachment foundAttachment = TryGetAttachmentInternal(attachmentName);
            return (foundAttachment != null) ? foundAttachment.Uri : null;
        }

        /// <summary>
        /// Creates the a new attachment instance.
        /// </summary>
        /// <param name="fileName">Specifies the attachment file name.</param>
        /// <param name="fileContent">Specifies the textual file content.</param>
        /// <returns>Returns the created attachment instance.</returns>
        public TfsAttachment CreateAttachment(string fileName, string fileContent)
        {
            PreCondition.AssertNotNullOrEmpty(fileName, "fileName");
            
            string attachmentLocalFilePath = CreateAttachmentFileOnDisk(fileName, fileContent);

            // remove all existing settings file
            RemoveAttachments(fileName);

            Attachment newAtta = new Attachment(attachmentLocalFilePath);
            _workItem.Attachments.Add(newAtta);
            return new TfsAttachment(newAtta, attachmentLocalFilePath);
        }

        /// <summary>
        /// Creates the a new attachment instance.
        /// </summary>
        /// <param name="fileId">Specifies the attachment file name.</param>
        /// <param name="comment">Specifies the comment of the new attachment to create.</param>
        /// <param name="localPath">Specifies the file location on the filesystem.</param>
        /// <returns>Returns the created attachment instance.</returns>
        public TfsAttachment CreateAttachment(int fileId, string comment, Uri localPath)
        {
            PreCondition.AssertNotNull(localPath, "localPath");

            // remove all existing settings file
            RemoveAttachmentById(fileId);

            Attachment newAtta = new Attachment(localPath.ToLocalPath());
            _workItem.Attachments.Add(newAtta);
            return new TfsAttachment(newAtta, localPath.ToLocalPath());
        }

        /// <summary>
        /// Creates the a new attachment instance.
        /// </summary>
        /// <param name="fileId">Specifies the current TFS attachment identifier number.</param>
        /// <param name="commentPrefix">Specifies an extension string which describes the old link of the attachment in the comment.</param>
        /// <param name="localPath">Specifies the file location on the filesystem.</param>
        /// <returns>Returns the created attachment instance.</returns>
        public TfsAttachment ReplaceAttachment(int fileId, string commentPrefix, string localPath)
        {
            PreCondition.AssertNotNullOrEmpty(localPath, "localPath");

            // remove all existing settings file
            RemoveAttachmentById(fileId);

            string comment = EvaluateComment(
                commentPrefix ?? string.Empty,
                localPath);

            Attachment newAtta = new Attachment(localPath, comment);
            _workItem.Attachments.Add(newAtta);
            return new TfsAttachment(newAtta, localPath);
        }

        private string EvaluateComment(string commentPrefix, string fileUniqueId)
        {
            string commentBase = TfsAttachment.CommentIdStart
                + fileUniqueId
                + TfsAttachment.CommentIdEnd;
            string commentBaseWithPrefix = commentPrefix
                + commentBase;

            if (commentBaseWithPrefix.Length < 255)
            {
                return commentBaseWithPrefix;
            }
            return commentBase;
        }

        private string CreateAttachmentFileOnDisk(string fileName, string fileContent)
        {
            string attachmentFilePath = Path.Combine(DataFolderUtil.CreateRandomFolderInTemp().FullName, fileName);

            using (StreamWriter textFile = File.CreateText(attachmentFilePath))
            {
                textFile.WriteLine(fileContent);
            }
            return attachmentFilePath;
        }

        private void RemoveAttachments(string fileName)
        {
            for (int i = _workItem.Attachments.Count - 1; i > -1; --i)
            {
                if (_workItem.Attachments[i].Name == fileName)
                    _workItem.Attachments.RemoveAt(i);
            }
        }

        private Attachment RemoveAttachmentById(int attachmentId)
        {
            for (int i = _workItem.Attachments.Count - 1; i > -1; --i)
            {
                if (TfsAttachment.TryGetIdFromAttachmentUri(_workItem.Attachments[i]) == attachmentId)
                {
                    Attachment toRemove = _workItem.Attachments[i];
                    _workItem.Attachments.RemoveAt(i);
                    return toRemove;
                }
            }
            return null;
        }

        private Attachment TryGetAttachmentInternal(string attachmentName)
        {
            DateTime lastWriteTime = DateTime.MinValue;
            Attachment foundAttachment = null;

            for (int i = 0; i < _workItem.Attachments.Count; ++i)
            {
                if (_workItem.Attachments[i].Name == attachmentName && _workItem.Attachments[i].LastWriteTime > lastWriteTime)
                {
                    foundAttachment = _workItem.Attachments[i];
                    lastWriteTime = _workItem.Attachments[i].LastWriteTime;
                }
            }
            return foundAttachment;
        }

        private string DownloadAttachment(Uri attachmentUri)
        {
            return WebRequest.Create(attachmentUri).TryDownloadTextData(_connectionInfo.NetworkCredentials);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------
        #endregion
    }
}